package com.minis.web.servlet;

import com.minis.beans.exception.BeansException;
import com.minis.beans.factory.annotation.Autowired;
import com.minis.web.context.WebApplicationContext;
import com.minis.web.context.support.AnnotationConfigWebApplicationContext;
import com.minis.web.context.support.XmlScanComponentHelper;
import com.minis.web.method.HandlerMethod;
import com.minis.web.method.annotation.RequestMappingHandlerAdapter;
import com.minis.web.method.annotation.RequestMappingHandlerMapping;
import com.minis.web.servlet.view.ModelAndView;
import com.minis.web.servlet.view.View;
import com.minis.web.servlet.view.ViewResolver;
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.*;

public class DispatcherServlet extends HttpServlet {

    // 方法一
//    private String sContextConfigLocation;
//    // 记录 URL 对应的 MappingValue 对象、对应的类和对应的方法
//    private Map<String, MappingValue> mappingValues;
//    private Map<String, Class<?>> mappingClz = new HashMap<>();
//    private Map<String, Object> mappingObjs = new HashMap<>();
//
//    @Override
//    public void init(ServletConfig config) throws ServletException {
//        super.init(config);
//
//        // 获取 xml 地址
//        sContextConfigLocation = config.getInitParameter("contextConfigLocation");
//        URL xmlPath = null;
//        try {
//            xmlPath = this.getServletContext().getResource(sContextConfigLocation);
//        } catch (MalformedURLException e) {
//            e.printStackTrace();
//        }
//        ClassPathXmlResource rs = new ClassPathXmlResource(xmlPath);
//        XmlConfigReader reader = new XmlConfigReader();
//        mappingValues = reader.loadConfig(rs);
//        // 解析 minisMVC-servlet.xml 中的 bean
//        Refresh();
//    }
//
//    private void Refresh() {
//        for (Map.Entry<String, MappingValue> entry : mappingValues.entrySet()) {
//            String id = entry.getKey();
//            String className = entry.getValue().getClz();
//            Object obj = null;
//            Class<?> clz = null;
//            try {
//                clz =  Class.forName(className);
//                obj = clz.getDeclaredConstructor().newInstance();
//            } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
//                e.printStackTrace();
//            }
//            mappingClz.put(id, clz);
//            mappingObjs.put(id, obj);
//        }
//    }
//
//    @Override
//    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        String sPath = req.getServletPath();
//        if (!this.mappingValues.containsKey(sPath)) {
//            return;
//        }
//        Class<?> clz = this.mappingClz.get(sPath);
//        Object obj = this.mappingObjs.get(sPath);
//        String methodName = this.mappingValues.get(sPath).getMethod();
//        Object objResult = null;
//        try {
//            Method method = clz.getMethod(methodName);
//            objResult = method.invoke(obj);
//        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
//            e.printStackTrace();
//        }
//        resp.getWriter().append(objResult.toString());
//    }




    // 方法二
//    private String sContextConfigLocation;
//
//    private List<String> packageNames = new ArrayList<>();
//    private Map<String,Object> controllerObjs = new HashMap<>();
//    private List<String> controllerNames = new ArrayList<>();
//    private Map<String,Class<?>> controllerClasses = new HashMap<>();
//    private List<String> urlMappingNames = new ArrayList<>();
//    private Map<String,Object> mappingObjs = new HashMap<>();
//    private Map<String,Method> mappingMethods = new HashMap<>();
//
//    // DispatcherServlet 启动的上下文
//    private WebApplicationContext webApplicationContext;
//    // Listener 启动的上下文
//    private WebApplicationContext parentApplicationContext;
//
//    @Override
//    public void init(ServletConfig config) throws ServletException {
//        super.init(config);
//        // 拿到 Listener 启动的时候注册好的 WebApplicationContext（Servlet 在 Listener 之后注册）
//        this.parentApplicationContext = (WebApplicationContext) this.getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
//
//        // 获取 MVC 用到的配置文件，如 minisMVC-servlet.xml，之后再扫描路径下的包
//        sContextConfigLocation = config.getInitParameter("contextConfigLocation");
//        URL xmlPath = null;
//        try {
//            xmlPath = this.getServletContext().getResource(sContextConfigLocation);
//        } catch (MalformedURLException e) {
//            e.printStackTrace();
//        }
//        this.packageNames = XmlScanComponentHelper.getNodeValue(xmlPath);
//        // 根据配置文件创建新的上下文
//        this.webApplicationContext = new AnnotationConfigWebApplicationContext(sContextConfigLocation, this.parentApplicationContext);
//        // 调用 refresh() 方法加载 Bean
//        Refresh();
//    }
//
//    protected void Refresh() {
//        // 初始化 controller
//        initController();
//        // 初始化 URL 映射
//        initMapping();
//    }
//
//
//    protected void initController() {
//        // 递归扫描包，获取所有的包名
//        this.controllerNames = scanPackages(this.packageNames);
//        for (String controllerName : controllerNames) {
//            Object obj = null;
//            Class<?> clz = null;
//            try {
//                clz = Class.forName(controllerName);
//                this.controllerClasses.put(controllerName, clz);
//            } catch (ClassNotFoundException e) {
//                e.printStackTrace();
//            }
//            try {
//                obj = clz.getDeclaredConstructor().newInstance();
//                populateBean(obj);
//                this.controllerObjs.put(controllerName, obj);
//            } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
//                e.printStackTrace();
//            }
//        }
//    }
//
//    // 处理 Autowired 注入（MVC 容器）
//    private void populateBean(Object bean) {
//        Class<?> clazz = bean.getClass();
//        Field[] fields = clazz.getDeclaredFields();
//        if (fields != null) {
//            for (Field field : fields) {
//                boolean isAutowired = field.isAnnotationPresent(Autowired.class);
//                if (isAutowired) {
//                    String fieldName = field.getName();
//                    Object autowiredBean = null;
//                    try {
//                        autowiredBean = this.webApplicationContext.getBean(fieldName);
//                    } catch (BeansException e) {
//                        e.printStackTrace();
//                    }
//                    // 设置属性值
//                    field.setAccessible(true);
//                    try {
//                        field.set(bean, autowiredBean);
//                    } catch (IllegalAccessException e) {
//                        e.printStackTrace();
//                    }
//                }
//            }
//        }
//    }
//
//    protected void initMapping() {
//        for (String controllerName : this.controllerNames) {
//            Class<?> clazz = this.controllerClasses.get(controllerName);
//            Object obj = this.controllerObjs.get(controllerName);
//            Method[] methods = clazz.getDeclaredMethods();
//            if (methods != null) {
//                for (Method method : methods) {
//                    boolean isRequestMapping = method.isAnnotationPresent(RequestMapping.class);
//                    if (isRequestMapping) {
//                        String urlMapping = method.getAnnotation(RequestMapping.class).value();
//                        this.urlMappingNames.add(urlMapping);
//                        this.mappingObjs.put(urlMapping, obj);
//                        this.mappingMethods.put(urlMapping, method);
//                    }
//                }
//            }
//        }
//    }
//
//    private List<String> scanPackages(List<String> packages) {
//        List<String> tempControllerNames = new ArrayList<>();
//        for (String packageName : packages) {
//            tempControllerNames.addAll(scanPackage(packageName));
//        }
//        return tempControllerNames;
//    }
//
//    private List<String> scanPackage(String packageName) {
//        List<String> tempControllerNames = new ArrayList<>();
//        URI uri = null;
//        try {
//            uri = this.getClass().getResource("/"
//                    + packageName.replaceAll("\\.", "/")).toURI();
//        } catch (Exception e) {
//        }
//        // 递归处理文件目录
//        File dir = new File(uri);
//        for (File file : dir.listFiles()) {
//            if(file.isDirectory()){
//                tempControllerNames.addAll(scanPackage(packageName + "." + file.getName()));
//            }else{
//                String controllerName = packageName + "."
//                        + file.getName().replace(".class", "");
//                tempControllerNames.add(controllerName);
//            }
//        }
//        return tempControllerNames;
//    }
//
//    @Override
//    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        String sPath = req.getServletPath();
//        if (!this.urlMappingNames.contains(sPath)) {
//            return;
//        }
//        Object obj = null;
//        Object objResult = null;
//        try {
//            Method method = this.mappingMethods.get(sPath);
//            obj = this.mappingObjs.get(sPath);
//            objResult = method.invoke(obj);
//        } catch (IllegalAccessException | InvocationTargetException e) {
//            e.printStackTrace();
//        }
//        resp.getWriter().append(objResult.toString());
//    }

    // 改造
    private static final long serialVersionUID = 1L;
    private static final String WEB_APPLICATION_CONTEXT_ATTRIBUTE = DispatcherServlet.class.getName() + ".CONTEXT";
    public static final String HANDLER_ADAPTER_BEAN_NAME = "handlerAdapter";
    public static final String VIEW_RESOLVER_BEAN_NAME = "viewResolver";
    private String sContextConfigLocation;

    private List<String> packageNames = new ArrayList<>();
    private Map<String,Object> controllerObjs = new HashMap<>();
    private List<String> controllerNames = new ArrayList<>();
    private Map<String,Class<?>> controllerClasses = new HashMap<>();

    // DispatcherServlet 启动的上下文
    private WebApplicationContext webApplicationContext;
    // Listener 启动的上下文
    private WebApplicationContext parentApplicationContext;

    // 映射处理器
    private RequestMappingHandlerMapping handlerMapping;
    // 处理器适配器
    private RequestMappingHandlerAdapter handlerAdapter;
    // 用于获取 view
    private ViewResolver viewResolver;

    public DispatcherServlet() {
        super();
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        // 拿到 Listener 启动的时候注册好的 WebApplicationContext（Servlet 在 Listener 之后注册）
        this.parentApplicationContext = (WebApplicationContext) this.getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);

        // 获取 MVC 用到的配置文件，如 minisMVC-servlet.xml，之后再扫描路径下的包
        sContextConfigLocation = config.getInitParameter("contextConfigLocation");
        URL xmlPath = null;
        try {
            xmlPath = this.getServletContext().getResource(sContextConfigLocation);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
        this.packageNames = XmlScanComponentHelper.getNodeValue(xmlPath);
        // 根据配置文件创建新的上下文
        this.webApplicationContext = new AnnotationConfigWebApplicationContext(sContextConfigLocation, this.parentApplicationContext);

        Refresh();
    }

    protected void Refresh() {
        initController();
        initHandlerMappings(this.webApplicationContext);
        initHandlerAdapters(this.webApplicationContext);
        initViewResolvers(this.webApplicationContext);
    }



    protected void initHandlerMappings(WebApplicationContext webApplicationContext) {
        this.handlerMapping = new RequestMappingHandlerMapping(webApplicationContext);
    }

    protected void initHandlerAdapters(WebApplicationContext webApplicationContext) {
        try {
            this.handlerAdapter = (RequestMappingHandlerAdapter)  webApplicationContext.getBean(HANDLER_ADAPTER_BEAN_NAME);
        } catch (BeansException e) {
            e.printStackTrace();
        }
    }

    protected void initViewResolvers(WebApplicationContext webApplicationContext) {
        try {
            this.viewResolver = (ViewResolver) webApplicationContext.getBean(VIEW_RESOLVER_BEAN_NAME);
        } catch (BeansException e) {
            e.printStackTrace();
        }
    }

//    protected void initController() {
//        // 递归扫描包，获取所有的包名
//        this.controllerNames = Arrays.asList(this.webApplicationContext.getBeanDefinitionNames());
//        for (String controllerName : controllerNames) {
//            try {
//                this.controllerClasses.put(controllerName, Class.forName(controllerName));
//            } catch (ClassNotFoundException e) {
//                e.printStackTrace();
//            }
//            try {
//                this.controllerObjs.put(controllerName, this.webApplicationContext.getBean(controllerName));
//            } catch (BeansException e) {
//                e.printStackTrace();
//            }
//        }
//    }

    protected void initController() {
        this.controllerNames = scanPackages(this.packageNames);
        for (String controllerName : this.controllerNames) {
            Object obj = null;
            try {
                obj = this.webApplicationContext.getBean(controllerName);
            } catch (BeansException e) {
                e.printStackTrace();
            }
            Class<?> clz = null;
            try {
                clz = Class.forName(controllerName);
//                obj = clz.getDeclaredConstructor().newInstance();
            } catch (Exception e) {
                e.printStackTrace();
            }
            this.controllerClasses.put(controllerName, clz);
            populateBean(obj);
            this.controllerObjs.put(controllerName, obj);
        }
    }

    // 处理controller中的@Autowired注解
    private void populateBean(Object obj) {
        Class<?> clz = obj.getClass();
        Field[] fields = clz.getDeclaredFields();
        for (Field field : fields) {
            boolean isAutowired = field.isAnnotationPresent(Autowired.class);
            if (isAutowired) {
                String fieldName = field.getName();
                Object autowiredBean = null;
                try {
                    autowiredBean = this.parentApplicationContext.getBean(fieldName);
                } catch (BeansException e) {
                    System.err.println("Failed to retrieve bean for field: " + fieldName + " in class: " + clz.getName());
                    e.printStackTrace();
                }
                field.setAccessible(true);
                try {
                    field.set(obj, autowiredBean);
//                    System.out.println("autowiredBean:" + autowiredBean);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }


    private List<String> scanPackages(List<String> packages) {
        List<String> tempControllerNames = new ArrayList<>();
        for (String packageName : packages) {
            tempControllerNames.addAll(scanPackage(packageName));
        }
        return tempControllerNames;
    }

    private List<String> scanPackage(String packageName) {
        List<String> tempControllerNames = new ArrayList<>();
        URI uri = null;
        try {
            uri = this.getClass().getResource("/"
                    + packageName.replaceAll("\\.", "/")).toURI();
        } catch (Exception e) {
        }
        // 递归处理文件目录
        File dir = new File(uri);
        for (File file : dir.listFiles()) {
            if(file.isDirectory()){
                tempControllerNames.addAll(scanPackage(packageName + "." + file.getName()));
            }else{
                String controllerName = packageName + "."
                        + file.getName().replace(".class", "");
                tempControllerNames.add(controllerName);
            }
        }
        return tempControllerNames;
    }

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) {
        request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.webApplicationContext);

        try {
            doDispatch(request, response);
        } catch (Exception e) {
            e.printStackTrace();
        }
        finally {
        }
    }

    // 通过 handlerMapping 获取到对应 handlerMethod，随后通过 HandlerAdapter 进行处理
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HandlerMethod handlerMethod = null;
        ModelAndView mv = null;

        handlerMethod = this.handlerMapping.getHandler(request);
        if (handlerMethod == null) {
            return;
        }

        HandlerAdapter ha = this.handlerAdapter;
        mv = ha.handle(request, response, handlerMethod);
        render(request, response, mv);
    }

    // 用 JSP 进行 render
    private void render(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) {
        if (mv == null) {
            try {
                response.getWriter().flush();
                response.getWriter().close();
                return;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        String sTarget = mv.getViewName();
        Map<String, Object> modelMap = mv.getModel();
        View view = resolveViewName(sTarget, modelMap, request);
        try {
            view.render(modelMap, request, response);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 根据 view 的名字找到实际的 view
    private View resolveViewName(String viewName, Map<String, Object> modelMap, HttpServletRequest request) {
        if (this.viewResolver != null) {
            View view = null;
            try {
                view = viewResolver.resolveViewName(viewName);
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (view != null) {
                return view;
            }
        }
        return null;
    }


}
